#include <iostream>
#include <fstream>
#include <iterator>
#include <cmath>
#include <cassert>
#include <cstdio>
#include <unistd.h>

#include "loNetList.hpp"
#include "sisInterface.h"

loNetList::loNetList()
  : edaSolution(),
    netlist_(NULL),
    modified_(true),
    cost_(-INFINITY)
{
}

loNetList::loNetList( const char *fname )
  : edaSolution(),
    modified_(true),
    cost_(-INFINITY)
{
  FILE *fp = NULL;
  fp = fopen( fname, "r" );
  assert( fp != NULL );
  netlist_ = sisBLIFToNetlist( fp );
  fclose( fp );
}

loNetList::loNetList( const loNetList &sol )
  : edaSolution(sol),
    modified_(true),
    cost_(-INFINITY)
{
  netlist_ = sisNetworkDup( sol.netlist_ );
}

loNetList::~loNetList()
{
  if ( netlist_ != NULL )
    sisNetworkFree( netlist_ );
}

void loNetList::init()
{
}

edaSolution* loNetList::clone() const
{
  edaSolution *ns = new loNetList(*this);
  return ns;
}

double loNetList::fullEvaluate()
{
  double nlits;
  if ( modified_ ){
    nlits = (double)sisNumberLiterals( netlist_ );
    modified_ = false;
    cost_ = -nlits;
  }
  return cost_;
}

double loNetList::partialEvaluate()
{
  return fullEvaluate();
}

void loNetList::Serialize( edaBuffer &buf, bool pack )
{
  char s[256];
  char fname[256];
  FILE *fp = NULL;

  edaSolution::Serialize(buf, pack);

  // prepare temporary file name
  sprintf( fname, ".netlist_%d.blif", getpid() );

  if (pack)
  {
    // save netlist to file using BLIF format
    fp = fopen( fname, "w" );
    sisNetlistToBLIF( netlist_, fp );
    fclose( fp );

    // read file in text format and serialize into buffer
    fp = fopen( fname, "r" );
    int n = 0;
    while( fgets( s, 256, fp ) != NULL ){
      n++;
    }
    fseek( fp, 0, SEEK_SET );
    buf.Pack( &n, 1 );
    while( fgets( s, 256, fp ) != NULL ){
      n = strlen(s) + 1;
      buf.Pack( &n, 1 );
      buf.Pack( s, n );
    }
    fclose( fp );
  }
  else
  {
    // deserialize text and save to a temporary BLIF file
    int n, l, i;
    buf.UnPack( &n, 1 );
    fp = fopen( fname, "w" );
    for( i=0; i<n; i++ ){
      buf.UnPack( &l, 1 );
      buf.UnPack( s, l );
      fprintf( fp, "%s", s );
    }
    fclose( fp );

    // open BLIF file into netlist
    fp = fopen( fname, "r" );
    netlist_ = sisBLIFToNetlist( fp );
    fclose( fp );
  }
}

void loNetList::printOn(ostream &os) const
{
  // prepare temporary file name
  char fname[256];
  sprintf( fname, ".netlist_%d.blif", getpid() );

  // write to temporary file
  FILE *fp = NULL;
  fp = fopen( fname, "w" );
  sisNetlistToBLIF( netlist_, fp );
  fclose( fp );

  // stream the file to ostream
  char s[256];
  fp = fopen( fname, "r" );
  while( fgets( s, 256, fp ) != NULL ){
    os << s;
  }
  fclose( fp );
}

edaSolution& loNetList::operator = (const edaSolution &sol)
{
  loNetList &mysol = (loNetList&)sol;
  netlist_ = sisNetworkDup( mysol.netlist_ );
  modified_ = true;
  return (*this);
}

bool loNetList::operator == (const edaSolution &sol) const
{
  // FIXME
  // This operator will be used for Tabu search
  // but a partial comparison can be performed
  loNetList &mysol = (loNetList&)sol;
  return (sisNetlistEquiv( mysol.netlist_, netlist_ )==1)? true: false;
}

void loNetList::setModified( bool status )
{
  modified_ = status;
}

void *loNetList::getNetlist()
{
  modified_ = true;
  return netlist_;
}
